﻿/*
   RxInputManager
   Copyright 2011 Jordan Neumeyer

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
   
   File: ControlScheme.cs 
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Xml.Linq;
using System.Diagnostics;
using RxInputManager.Extensions;

// TODO:
// Add alternative key back in.


namespace RxInputManager.ControlScheme
{

    public enum ControllerType : byte { None = 0, Controller = 1, KeyboardMouse = 2 }

    public class ControlScheme<TValue,TAction>
    {
        protected SortedDictionary<TAction,HashSet<TValue>> KeySets;

        public string ConfigurationName { get; protected set; }

        public ControlScheme()
        {
            KeySets = new SortedDictionary<TAction, HashSet<TValue>>();
            backupSet = new SortedDictionary<TAction, HashSet<TValue>>();
            ConfigurationName = String.Empty;
        }

        public ControlScheme(string configurationName, TAction[] actionsList) : this()
        {
            ConfigurationName = configurationName;
            var len = actionsList.Length;
            for(int i = 0; i < len; ++i)
            {
                KeySets.Add(actionsList[i], new HashSet<TValue>());
            }
        }

        public SortedDictionary<TAction,HashSet<TValue>> KeySet
        {
            get { return KeySets; }
        }

        public bool AddKey(TAction action, TValue key)
        {
            HashSet<TValue> tempKeySet;
            if (KeySets.TryGetValue(action, out tempKeySet))
            {
                return tempKeySet.Add(key);
            }
            return false;
        }

        public void AddKeys(TAction action, params TValue[] keys)
        {
            foreach (var key in keys)
            {
                AddKey(action, key);
            }
        }

        public bool RemoveKey(TAction action, TValue key)
        {
            HashSet<TValue> tempKeySet;
            if (KeySets.TryGetValue(action, out tempKeySet))
            {
                return tempKeySet.Remove(key);
            }
            return false;
        }

        public void RemoveKeys(TAction action, params TValue[] keys)
        {
            foreach (var key in keys)
            {
                RemoveKey(action, key);
            }
        }

        public void ClearAction(TAction action)
        {
            HashSet<TValue> tempKeySet;
            if (KeySets.TryGetValue(action, out tempKeySet))
            {
                tempKeySet.Clear();
            }
        }

        public void ClearAll()
        {
            ClearAll(this.KeySets);
        }

        protected static void ClearAll(SortedDictionary<TAction,HashSet<TValue>> keySet)
        {
            foreach (var keyHash in keySet.Values)
                keyHash.Clear();
        }

        protected SortedDictionary<TAction, HashSet<TValue>> backupSet;
        protected static void ReplaceSet(SortedDictionary<TAction, HashSet<TValue>> src, SortedDictionary<TAction, HashSet<TValue>> dst)
        {
            Debug.Assert(null != src && null != dst, "Sets cannot be null!");
            Debug.Assert(src.Keys == dst.Keys);

            ClearAll(dst);

            foreach (var key in src.Keys)
            {
                HashSet<TValue> value;
                if (dst.TryGetValue(key, out value))
                {
                    value.UnionWith(src[key]);
                }
            }
            
        }

        public void EnableKeys()
        {
            ReplaceSet(KeySets, backupSet);
            ClearAll(KeySets);
        }

        public void DisableKeys()
        {
            ReplaceSet(backupSet, KeySets);
        }

        public HashSet<TValue> this[TAction action]
        {
            get
            {
                HashSet<TValue> outTemp;
                if (KeySet.TryGetValue(action, out outTemp))
                {
                    return outTemp;
                }
                throw new KeyNotFoundException(String.Format("{0} action does not exist in this control scheme!", action));
            }
        }

        public void AddNewAction(TAction action)
        {
            KeySet.Add(action, new HashSet<TValue>());
        }
    }

    public static class ControlSchemeHelper
    {
        public static T[] GetAllValues<T>() 
        {
            var values = Enum.GetValues(typeof(T));
            T[] asTypedArray = new T[values.Length];
            Array.Copy(values, asTypedArray, values.Length);

            return asTypedArray;
        }
    }

    public class KeyboardMouseControlSchemeContainer<TAction>
    {
        private ControlScheme.ControlScheme<Keys, TAction> keyboard;
        public ControlScheme.ControlScheme<Keys, TAction> Keyboard
        {
            get { return keyboard; }
        }

        private ControlScheme.ControlScheme<MouseButtons, TAction> mouse;
        public ControlScheme.ControlScheme<MouseButtons, TAction> Mouse
        {
            get { return mouse; }
        }

        public KeyboardMouseControlSchemeContainer(ControlScheme.ControlScheme<Keys, TAction> k, ControlScheme.ControlScheme<MouseButtons, TAction> m)
        {
            mouse = m;
            keyboard = k;
        }

        public KeyboardMouseControlSchemeContainer()
        {
            mouse = new ControlScheme<MouseButtons, TAction>("Blah", ControlScheme.ControlSchemeHelper.GetAllValues<TAction>());
            keyboard = new ControlScheme<Keys, TAction>("Blah2", ControlScheme.ControlSchemeHelper.GetAllValues<TAction>());
        }
    }

}
